<?php declare(strict_types=1);

namespace App\Services\Invoice;


use App\Exceptions\BizException;
use App\Models\NuoNuoStore;
use App\Models\Order;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class NuoNuoInvoiceService
{

    public function invoice(string $orderNo): string
    {
        if (empty($orderNo)) {
            throw new BizException("订单号不存在");
        }
        $orders_billing = Db::table('orders_billing')->where(['out_trade_no' => $orderNo])->first(); // 有无此订单号开票记录
        if (!empty($orders_billing)) {
            return $orders_billing->billing_address;
        }

        $order = Order::query()->where(['out_trade_no' => $orderNo])->first(); // 查询订单信息
        if (empty($order)) {
            throw new BizException("订单号不存在");
        } else if ($order->pay_status != 1) {
            throw new BizException("订单未支付");
        }
        $nuonuo_stores = NuoNuoStore::query()->where(['store_id' => $order->store_id])->first(); // 查找记录
        if (empty($nuonuo_stores)) {
            throw new BizException("商户未配置诺诺开票参数");
        }
        // $salerTaxNum = '150301199811285326';

        /*
        $appKey = '13985881'; // appKey
        $appSecret = '5836FB60AB5A4D5D'; // appSecret
        */
        $appKey = $nuonuo_stores->app_id;

        $appSecret = $nuonuo_stores->app_secret;

        // $userTax = $nuonuo_stores->userTax; // 授权商户的税号
        $salerTaxNum = $nuonuo_stores->userTax; // 销售方税号
        $taxnum = $nuonuo_stores->userTax; // 授权商户的税号
        $token = $nuonuo_stores->access_token;
        if (empty($appKey) || empty($appSecret) || empty($token)) {
            throw new BizException("商户未配置参数");
        }
        // 离线扫码开票接口
//        $method = "nuonuo.ElectronInvoice.saveScanTemp"; // change to your method
        $method = "nuonuo.OpeMplatform.saveScanTemp";
        $body = [
            "orderNo" => $order->out_trade_no, // aliscan20220804105945037182529
            "salerTaxNum" => $salerTaxNum, // 150301199811285326
            "clerk" => $nuonuo_stores->clerk, // 开票人名称
            "remark" => $nuonuo_stores->remark,
            "invoiceDate" => $order->created_at,
            "detail" => json_encode([
                'goodsName' => $nuonuo_stores->goodsName,
                'withTaxFlag' => $nuonuo_stores->withTaxFlag, // 含税标志(0:不含税, 1:含税)
                'goodsCode' => $nuonuo_stores->goodsCode, // 商品编码
                'taxRate' => $nuonuo_stores->taxRate, // 税率（只支持 0.xx x）
                'taxIncludedAmount' => $order->total_amount,
            ]),
            "orderTotal" => $order->total_amount,
        ];
        $json = '{';
        foreach ($body as $k => $v) {
            if ($k == 'detail') {
                $json = $json . '"' . $k . '"' . ':' . '[' . $v . '],';
            } else {
                $json = $json . '"' . $k . '"' . ':' . '"' . $v . '",';
            }
        }
        // print_r($json);die;
        $json = rtrim($json, ",");
        $body = $json . '}';
        $url = "https://sdk.nuonuo.com/open/v1/services"; // https://sandbox.nuonuocs.cn/open/v1/services
        $senid = uniqid(); // generate your meaningful senid
        $result = $this->sendPostSyncRequest($url, $senid, $appKey, $appSecret, $token, $taxnum, $method, $body);

        Log::channel("invoice")->info("sendPostSyncRequest_invoice", [
            "config" => [
                "appKey" => $appKey,
                "appSecret" => $appSecret,
                "token" => $token,
                "method" => $method
            ],
            "req" => $body,
            "res" => $result
        ]);
        if ($result['code'] == 'E0000') {
            Db::table('orders_billing')->insert(['out_trade_no' => $orderNo, 'billing_address' => $result['result']['ewmUrl'], 'create_time' => time()]); // 开票地址存进数据库
        }
        return $result['result']['ewmUrl'] ?? "";
    }


    public function reInvoice(string $orderNo): array
    {
        if (empty($orderNo)) {
            throw new BizException("订单号不能为空");
        }
        $order = Order::query()->where(['out_trade_no' => $orderNo])->first(); // 查询订单信息
        if (empty($order)) {
            throw new BizException("订单不存在");
        }
        $nuonuo_stores = NuoNuoStore::query()->where(['store_id' => $order->store_id])->where(['is_billing' => 1])->first(); // 查找记录
        if (!$nuonuo_stores) {
            throw new BizException("商户未配置诺诺开票参数, 请先到代理商后台设置");
        }
        /*
        $appKey = '13985881'; // appKey
        $appSecret = '5836FB60AB5A4D5D'; // appSecret
        $salerTaxNum = $nuonuo_stores->userTax; // 销售方税号 150301199811285326
        */
        $appKey = $nuonuo_stores->app_id;
        $appSecret = $nuonuo_stores->app_secret;
        $taxnum = $nuonuo_stores->userTax; // 授权商户的税号
        $token = $nuonuo_stores->access_token;
        if (empty($appKey) || empty($appSecret) || empty($token)) {
            throw new BizException("商户未配置参数");
        }
//        $method = "nuonuo.ElectronInvoice.reInvoice"; // 开票重试接口
        $method = "nuonuo.OpeMplatform.reInvoice";
        $body = json_encode([
            "orderno" => $orderNo, // aliscan20220804105945037182529
        ]);
        $url = "https://sdk.nuonuo.com/open/v1/services"; // https://sandbox.nuonuocs.cn/open/v1/services
        $senid = uniqid(); // generate your meaningful senid
        $result = $this->sendPostSyncRequest($url, $senid, $appKey, $appSecret, $token, $taxnum, $method, $body);
        Log::channel("invoice")->info("sendPostSyncRequest_reInvoice", [
            "config" => [
                "appKey" => $appKey,
                "appSecret" => $appSecret,
                "token" => $token,
                "method" => $method
            ],
            "req" => $body,
            "res" => $result
        ]);
        return $result;
    }


    public function sendPostSyncRequest($url, $senid, $appKey, $appSecret, $token, $taxnum, $method, $content, $timeOut = 6): array
    {
        $this->checkParam($senid, "senid不能为空");
        $this->checkParam($token, "token不能为空");
        $this->checkParam($appKey, "appKey不能为空");
        $this->checkParam($method, "method不能为空");
        $this->checkParam($url, "请求地址URL不能为空");
        $this->checkParam($content, "content不能为空");
        $this->checkParam($appSecret, "appSecret不能为空");
        try {
            $timestamp = time();
            $nonce = rand(10000, 1000000000);
            $finalUrl = "{$url}?senid={$senid}&nonce={$nonce}&timestamp={$timestamp}&appkey={$appKey}";
            $urlInfo = parse_url($url);
            if ($urlInfo === FALSE) {
                throw new BizException("url解析失败");
            }
            $sign = $this->makeSign($urlInfo["path"], $appSecret, $appKey, $senid, $nonce, $content, $timestamp);
            $headers = [
                "Content-Type: application/json",
                "X-Nuonuo-Sign: {$sign}",
                "accessToken: {$token}",
                "userTax: {$taxnum}",
                "method: {$method}",
                "sdkVer: " . "2.0.0"
            ];
            return $this->postCurl($finalUrl, $content, $headers, $timeOut);
        } catch (\Exception $e) {
            throw new BizException("发送HTTP请求异常:" . $e->getMessage());
        }
    }

    public function checkParam($param, $errMsg): void
    {
        if (empty($param)) {
            throw new BizException($errMsg);
        }
    }

    public function makeSign($path, $appSecret, $appKey, $senid, $nonce, $body, $timestamp): string
    {
        $pieces = explode('/', $path);
        $signStr = "a={$pieces[3]}&l={$pieces[2]}&p={$pieces[1]}&k={$appKey}&i={$senid}&n={$nonce}&t={$timestamp}&f={$body}";

        $sign = base64_encode(hash_hmac("sha1", $signStr, $appSecret, true));
        return $sign;
    }


    /**
     * 以post方式发起http调用
     *
     * @param string $url url
     * @param array $params post参数
     * @param int $second url执行超时时间，默认30s
     * @throws NnuoException
     */
    private function postCurl($url, $params, $headers = array(), $second = 30): array
    {
        $ch = curl_init();
        $curlVersion = curl_version();

        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $params);

        if (!empty($headers)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }
        //运行curl
        $data = curl_exec($ch);
        if (empty($data)) {
            $error = curl_error($ch);
            curl_close($ch);
            throw new BizException("curl出错:$error");
        }
        curl_close($ch);
        $array = json_decode($data, true);
        if (empty($array)) {
            throw new BizException("数据格式异常");
        }
        return $array;
    }

}